package org.moonshot.log;

import java.io.PrintWriter;

public class Logger
{
	private final LogToken      token        = new LogToken (this);
	
	private static final StringBuilder errorBuilder = new StringBuilder (512);
	private static final StringBuilder debugBuilder = new StringBuilder (160);
	private static final StringBuilder warnBuilder  = new StringBuilder (160);
	
	private final PrintWriter   channel;
	private final Class<?>      source;
	private final Object        runtime;
	private final LogFactory    factory;
	
	public LogToken getToken ()
	{
		return token;
	}
	
	protected Logger (LogFactory aFactory, PrintWriter aChannel, Class<?> aSource)
	{
		this (aFactory, aChannel, aSource, aSource);
	}
	
	protected Logger (LogFactory aFactory, PrintWriter aChannel, Class<?> aSource, Object aRuntime)
	{
		factory = aFactory;
		channel = aChannel;
		source = aSource;
		runtime = aRuntime;
	}
	
	public void error (Throwable t)
	{
		error (token, t);
	}
	
	public void debug (String t)
	{
		debug (token, t);
	}
	
	public void warning (String t)
	{
		warning (token, t);
	}
	
	public void error (LogToken aRuntime, Throwable t)
	{
		final Class<?> aTarget = getTarget(aRuntime);
		if( qualifiesFor(aTarget, LogLevel.Error) )
		{
    		synchronized(errorBuilder)
    		{
    			errorBuilder.setLength (0);
    			appendCommon(errorBuilder, aTarget);
    			errorBuilder.append(t.getLocalizedMessage ());
    			channel.print (errorBuilder.toString ());
    		}
		}
	}
	
	public void debug (LogToken aRuntime, String t)
	{
		final Class<?> aTarget = getTarget(aRuntime);
		if( qualifiesFor(aTarget, LogLevel.Debug) )
		{
    		synchronized(debugBuilder)
    		{
    			debugBuilder.setLength (0);
    			appendCommon(debugBuilder, aTarget);
    			debugBuilder.append(t);
    			channel.print (debugBuilder.toString ());
    		}
		}
	}
	
	public void warning (LogToken aRuntime, String t)
	{
		final Class<?> aTarget = getTarget(aRuntime);
		if( qualifiesFor(aTarget, LogLevel.Warning) )
		{
    		synchronized(warnBuilder)
    		{
    			warnBuilder.setLength (0);
    			appendCommon(warnBuilder, aTarget);
    			warnBuilder.append(t);
    			channel.print (warnBuilder.toString ());
    		}
		}
	}
	
	private Class<?> getTarget(LogToken aToken)
	{
		if( aToken != null && aToken.logger.runtime != null )
			return aToken.logger.runtime.getClass ();
		
		return null;
	}
	
	private boolean qualifiesFor (Class<?> aSecond, LogLevel aLevel)
    {
	    return factory.getLogBit(source, aLevel) || factory.getLogBit(aSecond, aLevel);
    }

	private void appendCommon (StringBuilder aBuild, Class<?> aTarget)
    {
		aBuild.append(System.currentTimeMillis ());
		aBuild.append(' ');
		aBuild.append(source.getCanonicalName ());
		
		if( aTarget != source && aTarget != null )
		{
    		aBuild.append("/");
    		aBuild.append(aTarget.getClass ());
		}
		aBuild.append(": ");
    }
}
